﻿using System;
using System.Threading;
using Infrastructure.Messages;

namespace Infrastructure.Utility
{
    public static class FuncExtensions
    {
        public static T WithRetry<T>(this Func<T> action, int retryAttempts = 0, int delayInMilliSeconds = 0)
        {
            dynamic result = default(T);
            var retryCount = 0;

            var exception = new Exception();
            var successful = false;
            do
            {
                try
                {
                    result = action();
                    successful = true;
                }
                catch (Exception ex)
                {
                    retryCount++;
                    Thread.Sleep(delayInMilliSeconds);

                    if (retryCount == retryAttempts)
                        exception = ex;
                }
            } while (retryCount < retryAttempts && !successful);

            if (!successful && result is ResponseBase)
            {
                result = new ResponseBase();

                result.Success = false;
                result.Message = exception.Message;

                return result;
            }
            return result;
        }

        // Partial. 
        // Allows you to use a function to transform a function that 
        // takes N parameters into a function that takes N - 1 parameters.

        // Use Partial Application when you can bind a parameter early.  
        public static Func<TResult> Partial<TParam, TResult>(
            this Func<TParam, TResult> func, TParam parameter)
        {
            return () => func(parameter);
        }

        public static Func<TResult> Partial<TParam, TParam2, TResult>(
            this Func<TParam, TParam2, TResult> func, TParam parameter, TParam2 parameter2)
        {
            return () => func(parameter, parameter2);
        }

        // Currying - Named after Pascal Curry
        // Transform a function that takes N parameters into a parameter 
        // that you invoke to apply a parameter and get back as a result
        // a function that takes N-1 parameters. 

        // Think of this as returning a  function that will do the partial application 
        // function instead of immediately doing the partial application function.

        // Use currying only when you know the parameter values later in execution.
        public static Func<TParam, Func<TResult>> Curry<TParam, TResult>(
            this Func<TParam, TResult> func)
        {
            return parameter => () => func(parameter);
        }
    }
}